﻿using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Web.Configuration;
using System.Xml;

using TfsSpam.Utilities;
using TfsSpam.Differ;

namespace TfsSpam.Service
{
    /// <summary>
    /// The EventDispatcher class handles the task of dispatching all received
    /// events to a background worker thread for later processing by the
    /// <see cref="CheckinEventProcessor"/> class.
    /// </summary>
    internal static class EventDispatcher
    {
        #region -- Private class members
        /// <summary>
        /// The logger used to log information about the event dispatching.
        /// </summary>
        private static TfsLogger _logger = new TfsLogger();
        #endregion

        #region -- Private classes
        /// <summary>
        /// The EventData class wraps the state information that needs
        /// to be provided to the worker thread in order to process an
        /// event.
        /// </summary>
        private class EventData
        {
            #region -- Public fields
            /// <summary>
            /// The XML string recieved in the event notification.
            /// </summary>
            public readonly string EventXml;

            /// <summary>
            /// The absolute path of the request to our web service.
            /// </summary>
            public readonly string RequestPath;
            #endregion

            #region -- Constructor
            /// <summary>
            /// Standard constructor for the class, sets the public fields
            /// for the object.
            /// </summary>
            /// <param name="eventXml">The XML string recieved in the TFS event notification.</param>
            /// <param name="requestPath">The absolute path of the request to our web service.</param>
            public EventData( string eventXml, string requestPath )
            {
                EventXml = eventXml;
                RequestPath = requestPath;
            }
            #endregion
        }
        #endregion

        #region -- Public methods
        /// <summary>
        /// Dispatches an event notification to a background worker thread.
        /// </summary>
        /// <param name="eventXml">The information received about the event.</param>
        /// <param name="requestPath">The absolute path of the request made to the web service.</param>
        public static void Dispatch( string eventXml, string requestPath )
        {
            EventData data = new EventData( eventXml, requestPath );

            _logger.Verbose( "Queueing an event notification: {0} - {1}", requestPath, eventXml );
            ThreadPool.QueueUserWorkItem( new WaitCallback( EventWorker ), data );
            _logger.Verbose( "Successfully queued an event notification" );
        }
        #endregion

        #region -- Private methods
        /// <summary>
        /// This method runs on a worker thread from the standard .NET thread pool
        /// and handles the work of actually processing an event received from the
        /// TFS server we've registered for events from.
        /// </summary>
        /// <param name="workerData">The data passed along to the worker thread.</param>
        [SuppressMessage( "Microsoft.Design", "CA1031", Justification = "We want to catch all exceptions to log them for further troubleshooting" )]
        private static void EventWorker( object workerData )
        {
            int changesetId = TfsEventParser.InvalidChangesetId;

            _logger.Verbose( "Processing a checkin event notification" );

            try
            {
                EventData eventData = (EventData) workerData;
                CheckinEventProcessor eventProcessor;

                _logger.Verbose( "Determining the changeset id" );
                changesetId = TfsEventParser.GetChangesetId( eventData.EventXml );
                if ( changesetId != TfsEventParser.InvalidChangesetId )
                {
                    _logger.Information( "Service worker thread processing changeset id {0}", changesetId );
                    eventProcessor = new CheckinEventProcessor( changesetId, WebConfigurationManager.OpenWebConfiguration( eventData.RequestPath ) );

                    eventProcessor.ProcessChangeset();
                    _logger.Information( "Changeset {0} successfully processed", changesetId );
                }
                else
                {
                    _logger.Error( "Unable to get the id of the changeset from this notification data: {0}", eventData.EventXml );
                }
            }
            catch ( CheckinEventProcessorException cepex )
            {
                _logger.Error( "An error occurred when processing changeset '{0}': {1}", changesetId, cepex.Message );
                _logger.Verbose( "Detailed exception information: {0}", cepex.ToString() );
            }
            catch ( XmlException xex )
            {
                _logger.Error( "Unable to get the changeset id due to a XML parsing error: {0}", xex.Message );
            }
            catch ( ArgumentNullException anex )
            {
                _logger.Error( "Unable to process the checkin event noficiation: {0}", anex.Message );
            }
            catch ( Exception ex )
            {
                _logger.Critical( "General failure: {0}", ex.ToString() );
                throw;
            }
        }
        #endregion
    }
}
